一维数组

	数组是一批相同数据的集合
	一维数组定义方式：类型说明符	数组名[常量表达式]；
        如果说 int a; 是在内存中开辟了一个int大小的空间,
        那么 int a[10] 就是表示在内存中开辟了10个 连续的 int大小的空间,总大小为 10 * sizeof(int).
        注意：
			1.1数组开辟的空间是连续的。
			1.2数组的类型实际上是指数组元素的取值类型，对于同一个数组，其所有的元素的数据类型都是相同的
			1.3数组名不能与其它变量名相同
			1.4从下标0开始
			1.5不能在方括号中用变量来表示元素的个数，但是可以使用符号常熟或者常量表达式
				ex:#define FD  5
					int a[3+2],b[2+FD];
			1.6允许在同一个类型说明中，说明多个数组和多个变量。
	数组长度不能用变量定义是因为：
		C语言里，所有变量空间都是在程序运行之初向系统申请的，包括指针，
		除了一些动态申请方式如malloc函数。没有申请的空间系统是不允许读写的。
		那么如果一个数组大小是变量定义，则在程序运行前不能确定数组大小，
		也就无法申请，故不允许。所以，解决的办法便是通过malloc函数，即
		int a[n];
		可改为
		int* const a = (int*)malloc(sizeof(int)*n);
		这样a变量就完全可以当作一个数组直接使用了，包括a[1]之类的。
		因为“[]”中括号运算符做的只是偏移其内的地址数并取值。如：
		a[1]
		等价于
		(a + 1)
		而const修饰符在星号之后，则表示指针在被声明后不能改变，即只能指向这个获取的空间，完全符合数组的性质
		综上所述，如果需要变量控制长度的一个数组，类似“int a[n];”的，
		可改成“int const a = (int)malloc(sizeof(int)*n);”
		并直接作为数组以[]方式使用。const可以去掉，但要保证使用时不改变a的指向
		编写程序1：输入10个整数存入一维数组，再按逆序重新存放后输出
		#include<stdio.h>
		typedef int arrNum_10[10];
		int main()
		{
			int i,j;
			arrNum_10 arr;
			printf("请输入十个数组元素:\n");
			for(i = 0; i < 10;i++)
			{
				scanf("%d",&arr[i]);
			}
			for(i = 0; i < 10;i++)
			{
				printf("%d->",arr[i]);
			}
			printf("\n");
			for(i = 0;i < 10;i++)
			{
				for(j = i+1;j < 10;j++)
				{
					if(arr[j]>arr[i])
					{
						arr[j] = arr[j]^arr[i];
						arr[i] = arr[j]^arr[i];
						arr[j] = arr[j]^arr[i];
					}
				}
			}
			printf("排序后逆序输出：\n");
			for(i = 0; i < 10;i++)
			{
				printf("%d->",arr[i]);
			}
			return 0;
		}
		编写程序2：①程序运行时先显示Please input numbers:，
					再从键盘上读入一组整数(只考虑int型)，数与数之间只使用空格或回车作分隔。
					数可正可负，最多10000个，但若读入的数为-222时，则表示输入结束且-222不算在该组数内。
		　　		②对这一组数按从小到大的顺序进行排序。　　
					③将排序后的这一组数输出到屏幕上，输出格式为每行6个数，
					数与数之间使用逗号(,)分隔，两个逗号之间的宽度(不算逗号)为6且使用左对齐格式。
					注意，行尾没有逗号。
			#include<stdio.h>
			#include<malloc.h>
			
			int main()
			{
				int arr_len,i = 0,j,k,m = 0,flag=0;
				
				printf("Please input numbers:\n");
				scanf("%d",&arr_len);
				if(arr_len > 10000)
				{
					printf("输入的数组长度超过要求:\n");
					return 0;
				}
				typedef int arrNum[arr_len];
				arrNum  const arr = = (int *)malloc(sizeof(int)*arr_len);;
			//	arr = (int *)malloc(sizeof(int)*arr_len);
				if(arr == NULL)
				{
					printf("申请动态内存失败\n");
					return 0;
				}
				while(scanf("%d",&k) != -222)
				{
					arr[m++] = k;
					if(arr_len == m)
					{
						break;
					}
				}
				for(i = 0;i < arr_len;i++)
				{
					for(j = i+1;j < arr_len;j++)
					{
						if(arr[i] > arr[j])
						{
							arr[j] = arr[j]^arr[i];
							arr[i] = arr[j]^arr[i];
							arr[j] = arr[j]^arr[i];
						}
					}
				}
				for(i = 0;i < arr_len;i++)
				{
					//printf("%6d,",arr[i]);
					flag++;
					if((i+1) % 6 == 0)
					{
						printf("%-6d\n",arr[i]);
					}
					else
					{
						printf("%-6d,",arr[i]);
					}
				}
				return 0;
			}
二维数组
定义：
	由双下标变量定义的数组就称为二维数组，双下标变量就是数组的元素。
	二维数组定义的一般形式和一维数组大同小异，只是下表中的常量表达式多了一个（维度加一）：
	<类型标识符> <数组名标识符> [<常量表达式>] [<常量表达式>]
	双下标变量可以是以下组成：
		1.1可以是正的整形变量：eg：Array [2] [3]；
		1.2可以是字符常量：eg：Array [‘A’] [‘B’] = Array [65] [66]；
		1.3可以是有确定值的表达式：eg：Array [i+1] [j+2]、Array [ A[1] ] [ A[2] ]；
初始化：
	对二维数组的初始化有一下几种办法：
	2.1分行给二维数组赋值：这个方法用到对二维数组的降维理解，比如：int a [2] [3] = {{1, 2, 3}, {4, 5, 6}}；
	2.2可以只有一个花括号，按二维数组元素的排列顺序对各元素依次赋值 int a [2] [3] = {1, 2, 3, 4, 5, 6}；
	2.3对部分元素赋初值 int a [2] [3] = {{2}, {4}}；
	2.4定义数组时对第一维的长度可以不定义，但必须定义第二维的长度 int a [] [3] = {1， 2， 3， 4， 5， 6}；
	编写程序1：有一个3行4列的矩阵，要求编程求出其中的最大的那个元素值
				以及所在的行号和列号。(矩阵初始值通过键盘输入)
	
	#include<stdio.h>
	
	typedef int arrNum_3_4[3][4];
	int main()
	{
		int max_line,max_row,i,j,max;
		arrNum_3_4 arr;
		for(i = 0;i < 3;i++)
		{
			for(j = 0;j<4;j++)
			{
				scanf("%d",&arr[i][j]);
			}
		}
		for(i = 0;i < 3;i++)
		{
			for(j = 0;j<4;j++)
			{
				printf("%5d",arr[i][j]);
			}
			printf("\n");
		}
		max = arr[0][0];
		for(i = 0;i < 3;i++)
		{
			for(j = 0;j<4;j++)
			{
				if(arr[i][j] > max)
				{
					max = arr[i][j];
					max_line = i;
					max_row = j;
				}
			}
		}
		printf("此数组中最大值为%d = arr[%d][%d]\n",max,max_line,max_row);
	}
	编写程序2：输出以下杨辉三角(输出到第十行)
	1
	1	1
	1	2	1
	1	3	3	1
	1	4	6	4	1
	1	5	10	10	5	1
	...........
	#include<stdio.h>
	
	typedef int arrNum_10_10[10][10];
	int main()
	{
		arrNum_10_10 arr;
		int i,j,m;
		for(i =0;i<10;i++)
		{
			for(j=0;j<10;j++)
			{
				for(m =0;m<=i;m++)
				{
					if(j == i ||j ==0)
					{
						arr[i][j] = 1;
					}
					else
					{
						arr[i][j] = arr[i -1][j-1]+arr[i-1][j];
					}
				}	
			}		
		}
		for(i =0;i<10;i++)
		{
			for(j=0;j<=i;j++)
			{
				printf("%d\t",arr[i][j]);
			}
			printf("\n");
		}
	}
	
指针
指针是一个特殊的变量，它里面存储的数值被解释成为内存里的一个地址。
要搞清一个指针需要搞清指针的四方面的内容：
指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。
1.1指针的类型
	从语法的角度看，你只要把指针声明语句里的指针名字去掉，
	剩下的部分就是这个指针的类型。这是指针本身所具有的类型。
	(1)int ptr;//指针的类型是int
	(2)char ptr;//指针的类型是char
	(3)int ptr;//指针的类型是int
	(4)int *(ptr)[3];//指针的类型是int()[3]
	(5)int *(ptr)[4];//指针的类型是int()[4]
1.2指针所指向的类型
	通过指针来访问指针所指向的内存区时，指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
	从语法上看，你只须把指针声明语句中的指针名字和名字左边的指针声明符去掉，
	剩下的就是指针所指向的类型。
	(1)int ptr; //指针所指向的类型是int
	(2)char ptr; //指针所指向的的类型是char
	(3)int **ptr; //指针所指向的的类型是int
	(4)int *(ptr)[3]; //指针所指向的的类型是int()[3]
	(5)int *(ptr)[4]; //指针所指向的的类型是int()[4]
1.3指针的值----或者叫指针所指向的内存区或地址
	指针的值是指针本身存储的数值，这个值将被编译器当作一个地址，而不是一个一般的数值。
	在32 位程序里，所有类型的指针的值都是一个32 位整数，因为32 位程序里内存地址全都是32 位长。
	指针所指向的内存区就是从指针的值所代表的那个内存地址开始，长度为sizeof(指针所指向的类型)的一片内存区。
	以后，我们说一个指针的值是XX，就相当于说该指针指向了以XX 为首地址的一片内存区域；
	我们说一个指针指向了某块内存区域，就相当于说该指针的值是这块内存区域的首地址。
	指针所指向的内存区和指针所指向的类型是两个完全不同的概念。
1.4指针本身所占据的内存区
	用函数sizeof(指针的类型)测一下就知道了。在32 位平台里，指针本身占据了4 个字节的长度。
	指针本身占据的内存这个概念在判断一个指针表达式（后面会解释）是否是左值时很有用。
	
指针的算术运算
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的，以单元为单位。
	 char a[20]; 
	 int *ptr=(int )a; //强制类型转换并不会改变a的类型 
	 ptr++;  
	 指针ptr 的类型是int,它指向的类型是int，它被初始化为指向整型变量a。接下来的第3句中，
	 指针ptr被加了1，编译器是这样处理的：它把指针ptr 的值加上了sizeof(int)，在32 位程序中，
	 是被加上了4，因为在32 位程序中，int 占4 个字节。由于地址是用字节做单位的，
	 故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。
	 由于char 类型的长度是一个字节，所以，原来ptr 是指向数组a 的第0 号单元开始的四个字节，
	 此时指向了数组a 中从第4 号单元开始的四个字节。我们可以用一个指针和一个循环来遍历一个数组，
	ex:	#include<stdio.h>
		int main()
		{
			char a[20]="You_are_a_girl";
			int *ptr=a;
			char *p=a;
			printf("sizeof(int *ptr) = %d\n",sizeof(ptr));
			printf("sizeof(int *p) = %d\n",sizeof(p));				
		}
		sizeof(ptr)==sizeof(p)==4;
	ex：int array[20]={0};  
		int *ptr=array;  
		for(i=0;i<20;i++)  
		{  
			(*ptr)++;  
			ptr++；  
		}  
	ex：char a[20]=“You_are_a_girl”;  
		int *ptr=(int *)a;  
		ptr+=5; 
	#include<stdio.h>
	int main()
	{
		char a[20]=“You_are_a_girl”;  
		int *ptr=(int *)a; 
		printf("ptr =%p\n",ptr);		
		ptr+=5;//加的是20个地址
		printf("ptr = %p\t&ptr = %c\n",ptr,&ptr);
	}
	ptr 被加上了5，编译器是这样处理的：将指针ptr 的值加上5 乘sizeof(int)，
	在32 位程序中就是加上了5 乘4=20。由于地址的单位是字节，
	故现在的ptr 所指向的地址比起加5 后的ptr 所指向的地址来说，向高地址方向移动了20 个字节。
	在这个例子中，没加5 前的ptr 指向数组a 的第0 号单元开始的四个字节，
	加5 后，ptr 已经指向了数组a 的合法范围之外了。虽然这种情况在应用上会出问题，
	但在语法上却是可以的。这也体现出了指针的灵活性。如果上例中，ptr 是被减去5，
	那么处理过程大同小异，只不过ptr 的值是被减去5 乘sizeof(int)，
	新的ptr 指向的地址将比原来的ptr 所指向的地址向低地址方向移动了20 个字节。
	ex: #include<stdio.h>  
        int main()  
        {  
            char a[20]=" You_are_a_girl";  
            char *p=a;  
            char **ptr=&p;  
            //printf(“p=%d\n”,p);  
            //printf(“ptr=%d\n”,ptr);  
            //printf("*ptr=%d\n",*ptr);  
            printf("**ptr=%c\n",**ptr);  
            ptr++;  
            //printf(“ptr=%d\n”,ptr);  
            //printf("*ptr=%d\n",*ptr);  
            printf("**ptr=%c\n",**ptr);  
        }  
	正解: ptr 的类型是char ,指向的类型是一个char 类型,该指向的地址就是p的地址(&p),
	当执行ptr++;时,会使指针加一个sizeof(char),即&p+4;那(&p+4)指向哪呢?
	所以最后的输出会是一个随机的值,或许是一个非法操作.
	总结一下:
	一个指针ptrold 加(减)一个整数n 后，结果是一个新的指针ptrnew，ptrnew 的类型和ptrold 的类型相同，
	ptrnew 所指向的类型和ptrold所指向的类型也相同。
	ptrnew 的值将比ptrold 的值增加(减少)了n 乘sizeof(ptrold 所指向的类型)个字节。
	就是说，ptrnew 所指向的内存区将比ptrold 所指向的内存区向高(低)地址方向移动了n 乘sizeof(ptrold 所指向的类型)个字节。
	指针和指针进行加减：两个指针不能进行加法运算，这是非法操作，因为进行加法后，得到的结果指向一个不知所向的地方，
	而且毫无意义。两个指针可以进行减法操作，但必须类型相同，一般用在数组方面，
	
运算符&	
这里&是取地址运算符，是间接运算符。
&a 的运算结果是一个指针，指针的类型是a 的类型加个，指针所指向的类型是a 的类型，指针所指向的地址就是a 的地址。
总之p 的结果是p 所指向的东西，这个东西有这些特点：它的类型是p 指向的类型，它所占用的地址是p所指向的地址。
	ex：#include<stdio.h>
		int main()
		{
			int a=12; int b; int p; int ptr;  
			p=&a; //&a 的结果是一个指针，类型是int，指向的类型是  
			//int，指向的地址是a 的地址。  
			p=24; //p 的结果，在这里它的类型是int，它所占用的地址是  
			//p 所指向的地址，显然，p 就是变量a。  
			ptr=&p; //&p 的结果是个指针，该指针的类型是p 的类型加个，  
			//在这里是int 。该指针所指向的类型是p 的类型，这  
			//里是int。该指针所指向的地址就是指针p 自己的地址。  
			ptr=&b; //ptr 是个指针，&b 的结果也是个指针，且这两个指针  
			//的类型和所指向的类型是一样的，所以用&b 来给ptr 赋  
			//值就是毫无问题的了。  
			*ptr=34; //ptr 的结果是ptr 所指向的东西，在这里是一个指针，  
			//对这个指针再做一次运算，结果是一个int 类型的变量。 				
		}
指针表达式	
一个表达式的结果如果是一个指针，那么这个表达式就叫指针表达式。
	ex：#include<stdio.h>
	int main()
	{
		int a,b;  
		int array[10];  
		int pa;  
		pa=&a; //&a 是一个指针表达式。  
		Int **ptr=&pa; //&pa 也是一个指针表达式。  
		ptr=&b; //ptr 和&b 都是指针表达式。  
		pa=array;  
		pa++; //这也是指针表达式。 				
	}
	当一个指针表达式的结果是一个指针，已经明确地具有了指针自身占据的内存的话，
	这个指针表达式就是一个左值，否则就不是一个左值。在例七中，&a 不是一个左值，
	因为它还没有占据明确的内存。ptr 是一个左值，因为ptr 这个指针已经占据了内存，
	其实ptr 就是指针pa，既然pa 已经在内存中有了自己的位置，那么ptr 当然也有了自己的位置。
	




























	
	
	
	
	
	
	



